#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QMainWindow, QMessageBox
from PyQt4.QtCore import pyqtSignature, SIGNAL

from Ui_MainWin import Ui_MainWindow
from CreateDlg import CreateDialog
from JoinDlg import JoinDialog
from HelpDlg import HelpDialog
from ConfigDlg import ConfigDialog

from widget import *

import general

import sys, os
parpath=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(parpath)

from server import *
from client import *
from worker import WorkerThread

from config import INI

class GUI(QMainWindow, Ui_MainWindow):
    """
    图形界面主类
    """
    def __init__(self, parent = None):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        
        #与ui相连接的本地玩家对象
        self.player=None
        #开启的服务端或客户端对象
        self.server=None
        self.client=None
        
        #工作线程，用于开启服务器或客户端
        self.workerThread=WorkerThread()
        
        #用户选择的牌缓冲列表（牌id）
        self.bufCardList=[]
        
        #连接各服务器指令的信号槽
        self.connect(self, SIGNAL('showMessage(PyQt_PyObject)'), self.showMessage)
        self.connect(self, SIGNAL('takeCard(PyQt_PyObject)'), self.takeCard)
        self.connect(self, SIGNAL('playCard(PyQt_PyObject)'), self.playCard)
        self.connect(self, SIGNAL('showCard(PyQt_PyObject)'), self.showCard)
        self.connect(self, SIGNAL('setGameData(PyQt_PyObject)'), self.setGameData)
        self.connect(self, SIGNAL('updateGameData(PyQt_PyObject)'), self.updateGameData)
        self.connect(self, SIGNAL('clearOutList(PyQt_PyObject)'), self.clearOutList)
        self.connect(self, SIGNAL('addCard(PyQt_PyObject)'), self.addCard)
        self.connect(self, SIGNAL('win(PyQt_PyObject)'), self.win)
        
        self.connect(self, SIGNAL('requestCard(PyQt_PyObject)'), self.requestCard)
        self.connect(self, SIGNAL('resetData()'), self.reset)
        
        #当前令牌玩家id
        self.tokenPlayerId=0
        
        #玩家Widget列表
        self.wPlayerList=[]
        #本地玩家Widget
        self.wLPlayer=None
        #牌桌Widget
        self.tableWidget=TableWidget(self)
        self.setCentralWidget(self.tableWidget)
        #积分盒Widget列表
        self.wScoreBox=ScoreBoxWidget([
            self.scoreN, self.scoreS, self.scoreW,
            self.scoreE, self.scoreNW,self.scoreNE,
            self.scoreSW, self.scoreSE, self.scoreC
        ])

        #更新游戏模块列表，需在相关对话框创建前
        general.updateGameModules()

        #设置操作面板
        self.wAction.setParent(self.tableWidget)
        self.wToken.setParent(self.tableWidget)
        
        self.joinDialog=JoinDialog(self)
        self.createDialog=CreateDialog(self)
        self.helpDialog=HelpDialog()
        
        #设置显示菜单
        self.aView.addAction(self.toolBar.toggleViewAction())
        self.aView.addSeparator()
        self.aView.addAction(self.dockBoard.toggleViewAction())
        self.aView.addAction(self.dockConsole.toggleViewAction())
        
        #从配置文件更新配置
        self.updateConfig()
        
        #初始化各部件
        self.reset()
        

####以下为业务逻辑调用####

    def runServer(self, args):
        '''运行服务器，新线程中执行'''
        mGame=args['mGame']
        if not self.server:
            self.server=GameServer()
        server=self.server
        server.reset()
        server.configure(game=mGame.Game(), localPlayer=self.player, **args)
        server.create()
        server.waitReady()
        server.start()
        server.reset()
    
    def runClient(self, args):
        '''运行客户端，新线程中执行'''
        if not self.client:
            self.client=GameClient()
        client=self.client
        client.reset()
        client.setLocalPlayer(self.player)
        client.join(**args)

####以下为转换业务逻辑信号和槽处理####

    def resetData(self):
        '''通知界面重置，由业务逻辑层跨线程调用'''
        self.emit(SIGNAL('resetData()'))

    def getCardFromUser(self, msg=u'请选择牌：'):
        '''从用户操作获得牌，由业务逻辑层跨线程调用'''
        self.emit(SIGNAL('requestCard(PyQt_PyObject)'), msg)
        self.workerThread.join()
        if not self.workerThread.running:
            raise Exception('get card error!')
        return self.takeBufCardList()
    
    def requestCard(self, msg):
        '''槽，处理请求出牌时的界面响应'''
        #TODO:show some button
        self.wLPlayer.clearOutWCards()
        self.lbMsg.setText(msg)
        self.moveWAction()
        self.wAction.show()
    
    @pyqtSignature("")
    def on_btnAccept_clicked(self):
        """
        操作面板确定按钮被点击
        """
        if not self.workerThread.waiting:
            #工作线程没有等待数据，直接返回
            return
        self.wAction.hide()
        self.putBufCardList()
        self.workerThread.wakeUp()

    def putBufCardList(self):
        '''将选牌id装入牌缓冲列表'''
        wcards=self.wLPlayer.getStandOutWCardList()
        cards=[cw.id for cw in wcards]
        self.bufCardList=cards
    
    def takeBufCardList(self):
        '''从牌缓冲列表取走牌id'''
        cards=self.bufCardList
        self.bufCardList=[]
        return cards

    def updateData(self, cmd, data):
        '''转换UPDATEDATA指令为各种信号，实现线程间安全调用'''
        if cmd==SHOWMESSAGE:
            self.emit(SIGNAL('showMessage(PyQt_PyObject)'), data)
        elif cmd==TAKECARD:
            self.emit(SIGNAL('takeCard(PyQt_PyObject)'), data)
        elif cmd==PLAYCARD:
            self.emit(SIGNAL('playCard(PyQt_PyObject)'), data)
        elif cmd==SHOWCARD:
            self.emit(SIGNAL('showCard(PyQt_PyObject)'), data)
        elif cmd==SETGAMEDATA:
            self.emit(SIGNAL('setGameData(PyQt_PyObject)'), data)
        elif cmd==UPDATEGAMEDATA:
            self.emit(SIGNAL('updateGameData(PyQt_PyObject)'), data)
        elif cmd==CLEAROUTLIST:
            self.emit(SIGNAL('clearOutList(PyQt_PyObject)'), data)
        elif cmd==ADDCARD:
            self.emit(SIGNAL('addCard(PyQt_PyObject)'), data)
        elif cmd==WIN:
            self.emit(SIGNAL('win(PyQt_PyObject)'), data)
    
    def showMessage(self, msg):
        '''槽，SHOWMESSAGE指令对应动作'''
        self.console.append(msg)
    
    def takeCard(self, playerId):
        '''槽，TAKECARD指令对应动作'''
        self.wPlayerList[playerId].takeWCard(self.takeCardAm, self.takeCardTime)
        
    def playCard(self, data):
        '''槽，PLAYCARD指令对应动作'''
        playerId, cards=data
        self.wPlayerList[playerId].playWCard(cards, self.playCardAm, self.playCardTime)

    def showCard(self, data):
        '''槽，PLAYCARD指令对应动作'''
        playerId, cards=data
        self.wPlayerList[playerId].showWCard(cards)

    def setGameData(self, data):
        '''槽，SETGAMEDATA指令对应动作'''
        #排列显示玩家Widget
        self.orderWPlayerList()
        self.showWPlayerList()
        
        #设置积分盒
        scoreBox=data['scoreBox']
        self.setWScoreBox(scoreBox)
        
        #显示某些游戏数据
        msg=\
u'''Game: %s
Player count: %d
Card pack: %d
Card base: %d
Game round: %d
'''%(data['name'], data['playerCount'], data['cardPack'], data['cardBase'], data['round'])
        self.showMessage(msg)
        
    def updateGameData(self, data):
        '''槽，UPDATEGAMEDATA指令对应动作'''
        #TODO:update game data
        #更新持牌数、收牌列表
        playerCardCount=data['playerCardCount']
        inList=data['inList']
        i=0
        for pw in self.wPlayerList:
            pw.sit()
            pw.updateWInfo(cardCount=playerCardCount[i])
            pw.showWInfo()
            pw.updateInList(inList[i])
            pw.showInList()
            i+=1
        
        #更新积分盒
        scoreBox=data['scoreBox']
        self.setWScoreBox(scoreBox)
        
        #移动Token
        self.tokenPlayerId=data['tokenPlayerId']
        self.moveWToken()
        self.wToken.show()
        self.wToken.raise_()

    def clearOutList(self, data):
        '''槽，清空牌桌所有出牌列表'''
        for player in self.wPlayerList:
            player.clearOutWCards()

    def addCard(self, cards):
        '''槽，本地玩家Widget添加持牌Widget'''
        self.wLPlayer.addWCard(cards)

    def win(self, playerId):
        '''槽，WIN指令对应动作'''
        #显示玩家胜利提示框
        id=playerId
        name=self.wPlayerList[id].info['name']
        msg=u"""此局结束，玩家[%d]: %s 赢！\n提示：打开准备状态继续游戏。"""%(id, name)
        QMessageBox.information(self,
            self.trUtf8("游戏消息"),
            self.trUtf8(msg),
            QMessageBox.StandardButtons(\
                QMessageBox.Ok),
            QMessageBox.Ok)


    def updateToolState(self):
        '''更新工具栏选项的显示状态，跨线程调用'''
        self.aReady.setChecked(self.player.ready)
        self.aUseAI.setChecked(self.player.useAi)


####以下为界面操作####

    def updateConfig(self):
        '''从配置文件更新配置'''
        self.takeCardAm=INI.gui.takeCardAm
        self.playCardAm=INI.gui.playCardAm
        self.takeCardTime=INI.gui.takeCardTime
        self.playCardTime=INI.gui.playCardTime

    def resetWorker(self):
        '''重置工作线程'''
        if self.workerThread.isAlive():
            self.workerThread.stop()
        if self.server:
            self.server.reset()
        if self.client:
            self.client.reset()

    def reset(self):
        '''重置UI'''
        self.bufCardList=[]
        
        self.clearWPlayerList()
        self.wScoreBox.setAvailScores([])
        self.wScoreBox.initScoreWidgets()
        
        self.wAction.hide()
        
        self.wToken.hide()
        self.wToken.resize(CARDWIDTH, CARDHEIGHT)
        
        self.console.clear()
        
    def setWScoreBox(self, scoreBox):
        '''设置积分盒（分值）'''
        if not scoreBox:
            return
        for i in range(len(self.wPlayerList)):
            dirc=self.wPlayerList[i].dirc
            score=scoreBox[i]
            self.wScoreBox.setScore(dirc, score)

    def showWPlayerList(self):
        '''显示玩家Widget列表'''
        for pw in self.wPlayerList:
            pw.sit()
            pw.show()
    
    def clearWPlayerList(self):
        '''清理玩家Widget列表'''
        for pw in self.wPlayerList:
            pw.clear()
        self.wPlayerList=[]

    def orderWPlayerList(self):
        '''整理玩家Widget列表，从本地玩家处获取游戏数据'''
        playerCount=self.player.gameData['playerCount']
        playerInfo=self.player.gameData['playerInfo']
        cardOrder=self.player.gameData['cardOrder']
        #生成各玩家Widget
        for pinfo in playerInfo:
            if pinfo['id']==self.player.info['id']:
                pw=LPlayerWidget(parent=self.tableWidget, info=pinfo)
                pw.setCardOrder(cardOrder)
            else:
                pw=PlayerWidget(parent=self.tableWidget, info=pinfo)
            self.wPlayerList.append(pw)
        self.wPlayerList.sort(key=lambda x:x.info['id'])
        
        #根据玩家数和本地玩家号安排座位
        dircs=[]
        if playerCount==2:
            dircs=[S, N]
        elif playerCount==3:
            dircs=[S, NE, NW]
        elif playerCount==4:
            dircs=[S, E, N, W]
        elif playerCount==5:
            dircs=[S, E, NE, NW, W]
        elif playerCount==6:
            dircs=[S, SE, NE, N, NW, SW]
        elif playerCardCount==7:
            dircs=[S, SE, E, NE, NW, W, SW]
        elif playerCardCount==8:
            dircs=[S, SE, E, NE, N, NW, W, SW]
        #设置有效积分盒
        self.wScoreBox.setAvailScores(dircs)
        self.wScoreBox.initScoreWidgets()
        
        for i in range(playerCount):
            if type(self.wPlayerList[i])==LPlayerWidget:
                break
        #此时i为本地玩家Widget编号
        self.wLPlayer=self.wPlayerList[i]
        for dirc in dircs:
            self.wPlayerList[i].dirc=dirc
            i=(i+1)%playerCount

    def moveWAction(self):
        '''移动操作面板到合适位置'''
        self.wAction.adjustSize()
        if not self.wLPlayer:
            return
        offset=15
        rect=self.wAction.rect()
        x, y=self.wLPlayer.coordAction
        x=x-rect.width()/2
        y=y-rect.height()-offset
        self.wAction.move(x, y)
        self.wAction.raise_()
    
    def moveWToken(self):
        '''移动令牌到某玩家'''
        if not self.wPlayerList:
            return
        coord=self.wPlayerList[self.tokenPlayerId].coordPlay[:]
        self.wToken.move(*coord)

####以下为菜单或工具栏响应处理####

    def startWorkerThread(self, method, args):
        '''开启工作线程'''
        if self.workerThread.isAlive():
            self.resetWorker()
            
            QMessageBox.warning(self,
                self.trUtf8("系统消息"),
                self.trUtf8("""程序未准备好，请稍候再试。"""),
                QMessageBox.StandardButtons(\
                    QMessageBox.Ok),
                QMessageBox.Ok)
        else:
            self.workerThread=WorkerThread()
            self.workerThread.setMethod(method, args)
            self.workerThread.start()


    @pyqtSignature("")
    def on_aCreate_triggered(self):
        """
        创建游戏菜单被点击
        """
        dlg=self.createDialog
        if dlg.exec_():
            self.reset()
            args=dict(
                mGame=dlg.mGame, 
                listenAddress=dlg.host, 
                listenPort=dlg.port, 
                playerCount=dlg.playerCount, 
                netPlayerCount=dlg.netPlayerCount, 
                cardPack=dlg.cardPack, 
                cardBase=dlg.cardBase, 
            )
            self.startWorkerThread(self.runServer, (args, ))
            

    @pyqtSignature("")
    def on_aJoin_triggered(self):
        """
        加入游戏菜单被点击
        """
        dlg=self.joinDialog
        if dlg.exec_():
            self.reset()
            args=dict(
                server=dlg.host, 
                port=dlg.port, 
            )
            self.startWorkerThread(self.runClient, (args, ))
    
    @pyqtSignature("")
    def on_aRule_triggered(self):
        """
        游戏规则菜单被点击
        """
        self.helpDialog.show()
    
    @pyqtSignature("")
    def on_aAbout_triggered(self):
        """
        关于菜单被点击
        """
        QMessageBox.about(self,
            self.trUtf8("关于python-poker"),
            self.trUtf8("""python语言编写的联机扑克游戏。

作者：vvonder"""))

    @pyqtSignature("")
    def on_aStop_triggered(self):
        """
        结束游戏菜单被点击
        """
        self.resetWorker()
        self.reset()
    
    @pyqtSignature("")
    def on_aConfig_triggered(self):
        """
        设置菜单被点击
        """
        dlg=ConfigDialog(self)
        if dlg.exec_():
            self.updateConfig()
            self.player.updateConfig()
            if self.server:
                self.server.updateConfig()
                for player in self.server.players:
                    player.updateConfig()
            if self.client:
                self.client.updateConfig()
        
    @pyqtSignature("bool")
    def on_aReady_toggled(self, p0):
        """
        准备状态切换
        """
        if self.player:
            self.player.ready=p0
    
    @pyqtSignature("bool")
    def on_aUseAI_toggled(self, p0):
        """
        自动出牌切换
        """
        if self.player:
            self.player.useAi=p0


if __name__ == "__main__":
    from player import LocalPlayer
    localPlayer=LocalPlayer()
    app=QtGui.QApplication(sys.argv)
    gui=GUI()
    localPlayer.setUI(gui)
    gui.show()
    sys.exit(app.exec_())
